package com.h2cloud.JUCTest;

import java.util.HashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Package: com.h2cloud.JUCTest
 * @ClassName: Parker
 * @Author: h2cloud
 * @CreateTime: 2022/9/30 11:24 下午
 * @Description:
 */
// 自己动手实现自己的LockSupport
// http://www.codebaoku.com/it-java/it-java-259668.html
public class Parker {

	public static void main(String[] args) throws InterruptedException {
//		test1();
		test2();
	}

	// 先 park，再 unpark
	public static void test1() throws InterruptedException {
		Parker parker = new Parker();
		Thread thread = new Thread(() -> {
			System.out.println("park 之前");
			parker.park(); // park 函数可以将调用这个方法的线程挂起
			System.out.println("park 之后");
		});
		thread.start();
		TimeUnit.SECONDS.sleep(5);
		System.out.println("主线程休息了 5s");
		System.out.println("主线程 unpark thread");
		parker.unpark(thread); // 主线程将线程 thread 唤醒 唤醒之后线程 thread 才可以继续执行
	}

	// 先 unpark，再 park
	public static void test2() {
		Parker parker = new Parker();
		Thread thread = new Thread(() -> {
			try {
				TimeUnit.SECONDS.sleep(5);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("park 之前");
			parker.park(); // 线程 thread 后进行 park 操作
			System.out.println("park 之后");
		});
		thread.start();
		System.out.println("主线程 unpark thread");
		parker.unpark(thread); // 先进行 unpark 操作
	}

	private final ReentrantLock lock;
	private final HashMap<Thread, Integer> permits; // 每个线程的许可证的数量
	private final HashMap<Thread, Condition> conditions; // 用于唤醒和阻塞线程的条件变量

	public Parker() {
		lock = new ReentrantLock();
		permits = new HashMap<>();
		conditions = new HashMap<>();
	}

	// 对许可证 -1
	public void park() {
		Thread t = Thread.currentThread();
		// 如果还没有线程对应的 condition的话就进行创建
		if (conditions.get(t) == null) {
			conditions.put(t, lock.newCondition());
		}
		lock.lock();
		try {
			if (permits.get(t) == null || permits.get(t) == 0) {
				// 对许可证为变为 -1，park
				permits.put(t, -1);
				conditions.get(t).await();
			} else if (permits.get(t) == 1) {
				permits.put(t, 0);
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}

	public void unpark(Thread thread) {
		Thread t = thread; // 给线程 thread 发放一个许可证
		lock.lock();
		try {
			// 如果还没有创建许可证变量 说明线程当前的许可证数量等于初始数量也就是0
			// 因此方法许可证之后 许可证的数量为 1
			if (permits.get(t) == null || permits.get(t) == 0) {
				permits.put(t, 1);
			} else if (permits.get(t) == -1) {
				// 对许可证变为 0，unpark
				permits.put(t, 0);
				conditions.get(t).signal();
			}
		} finally {
			lock.unlock();
		}
	}
}
